# -*- coding:utf-8 -*-
import copy
import re

# @Time    : 2023/5/10 13:54
# @Author  : zengwenjia
# @Email   : zengwenjia@lingxi.ai
# @Software: LLM_internal

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

from bot.insurance_planner_gpt.agent.base_agent import LLMAgent
from typing import Dict, List
import json

# 2.不要忘记你的业务范围，你当前的服务范围仅是退休养老规划，社保计算和保险规划，你当前没有能力也不能给出其他范围任何问题（包含咨询天气、政治、语文、历史、数学题计算等等）的解决方案，对于其他范围的问题，你必须拒绝回答。

gpt_template = """
请记住，你是{planner_role}，性格活泼热情，细心周到。当前在企微场景和用户进行沟通。
你的公司是：{company_name}
业务范围是：{company_business}
公司的价值观是：{company_values}
你需要结合【用户信息】和【会话历史】，从【方案背景知识】和【下一步沟通方案】里选择内容进行组合，得到回复用户的话术。
需要注意以下事项：
1.可以适当用些表情来使对话的氛围更轻松，但表情别过于单一，以及增加你向用户传递的热情，表达的内容足够通俗易懂且口语化，随和、高中文化可理解，尽量表现出足够的亲和力。
2.每次沟通回复的话术不要每次开头重复用户的信息或内容，也不要在结尾强调自己的专业性。在和用户初始沟通阶段尽可能回复话术的简洁易懂
3.你具备向用户主动推荐保险产品的能力，用户主动要求了解或推荐保险产品时，你需要向用户推荐具体的保险产品，但投资、股票或基金的产品你均不懂，因此不要回答或主动推荐任何投资、股票或基金产品相关的问题
4.你足够的耐心和细心，在讲解解决方案或收集相关信息时，尽量向用户说明原因
5.根据会话历史和当前的会话阶段做出回应,不要每次都重复遍用户说过的信息。只产生{planner_role}的话术，不要产生用户的对话
6.对于用户自己还没有明确的产品解决方案的，需要逐步和用户确认及明确用户的需求，解决方案需要逐步由粗颗粒度到细颗粒度
7.措辞不要太直接和沉重，例如不要说'死亡赔偿'，而是说'身故赔付'。
8.讲解保险责任时不要拿用户举例，例如不能说'如果您发生意外/身故'，而是要说'如果被保险人发生意外/身故'
9.推荐的产品名称只能在【下一步沟通方案】或者【方案背景知识】中获取，如果这里面不存在的，都不准提及。
10.如果说到用户的信息，一定得保证是在'历史对话中已收集到的用户信息：'中的，其他任何地方都不提供用户信息，也不要自己编造用户信息
11.对于保险相关的知识只能根据【方案背景知识】和【下一步沟通方案】的内容进行回答，不能自己编造任何保险相关的知识
12、【方案背景知识】里的【{company_name}-公司知识】是相关的话术，你回答的话术尽可能从这里面进行组合拼接，包括：
- 沟通策略的回复话术
- 用户问题的回复话术

    
{user_info}
{next_solution}
{solution_text}
{conversation_history}
{planner_role}:
"""


class Conversation(LLMAgent):
    def __init__(self, planner_role, company_name, company_business, company_values,
                 conversation_history, next_solution, solution_text, user_info):
        self.company_name = company_name
        self.company_business = company_business
        self.company_values = company_values
        self.next_solution = next_solution
        self.solution_text = solution_text

        if solution_text:
            self.solution_text = "\n\n【方案背景知识】：\n" + solution_text
        else:
            self.solution_text = ""

        if next_solution:
            self.next_solution = "\n\n【下一步沟通方案】：\n====\n" + next_solution+"\n===="
        else:
            self.next_solution = ""

        if user_info:
            self.user_info = "\n\n【历史对话中已收集到的用户信息】：\n" + str(user_info)
        else:
            self.user_info = ""

        if conversation_history:
            self.conversation_history = "\n\n【会话历史】：\n" + conversation_history
        else:
            self.conversation_history = ""

        self.prompt = gpt_template.format(planner_role=planner_role,
                                          company_name=self.company_name, company_business=self.company_business,
                                          company_values=self.company_values, next_solution=self.next_solution,
                                          solution_text=self.solution_text, conversation_history=self.conversation_history,
                                          user_info=self.user_info
                                          )
        super().__init__(self.prompt)

    async def extract_numbers_and_links(self,text):
        # 先把文本里的数字编号替换成空，小数也会被换掉，目前是没问题的，只看数字编号的整数 TODO
        text = re.sub(r'\d+[、.]\d+', '', text)
        text = re.sub(r'\d+[、.]', '', text)
        numbers_pattern = re.compile(r'\d+')  # 简化：直接匹配连串数字
        links_pattern = re.compile(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+(?<!\))')
        numbers = re.findall(numbers_pattern, text)
        links = re.findall(links_pattern, text)
        return numbers, links

    async def check_reply_inclusion(self, reply_text):
        # 查看生成话术里的数字和链接是否都在背景知识里
        # 背景知识包括我们给的方案背景知识、下一步沟通方案、用户信息、对话历史
        background_text = self.user_info + self.solution_text + self.next_solution + self.conversation_history
        # 提取 背景知识 和 生成话术 中的数字和链接
        background_numbers, background_links = await self.extract_numbers_and_links(background_text)
        background_numbers = set(background_numbers)
        reply_numbers, reply_links = await self.extract_numbers_and_links(reply_text)
        all_included = True
        # print('背景知识的：', background_numbers, background_links,
        #       '\n回复的：', reply_numbers, reply_links)
        # 检查链接
        for link in reply_links:
            if link not in background_links:
                print(f"\n ---- 【话术异常】：链接 '{link}' 不在背景知识中----\n 话术是:{reply_text}\n")
                return False
        # 检查数字
        for number in reply_numbers:
            if number not in background_numbers:
                print(f"\n ---- 【话术异常】：数字 '{number}' 不在背景知识中----\n 话术是:{reply_text}\n")
                return False
        return all_included


if __name__ == '__main__':
    conversation_history_str="""
    用户：你是什么公司的
    犀心小助：
    """
    import asyncio
    planner_role: str = "专属保险助理"
    company_name: str = "慧择保险网"
    company_business: str = "仅为用户提供全面的保险规划服务，帮助用户解决任何保险规划或保险产品等相关的问题，以解决用户问题为目标，不以销售产品为导向。"
    company_values: str = "真诚、温暖、专业，做用户的真朋友，尊重每个用户个体的差异，用心为用户提供最优质的服务。"
    next_solution=''
    solution_info=''
    user_info_cn = ''

    conversation=Conversation(planner_role, company_name,company_business,
                 company_values, conversation_history_str,
                 next_solution,
                 solution_info,
                 user_info_cn)

    result = asyncio.run(conversation.achat_auto_llm(type="self"))

    print(result)
